﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Data;
using StyleMVVM.Data.Collections;
#if !DOT_NET
using Windows.Foundation.Collections;
using Windows.Foundation.Metadata;

#endif

namespace StyleMVVM.View.Regions
{
	public sealed class RegionMap : IObservableMap<string, IRegion>, IRegionMap
	{
		private readonly Dictionary<string, WeakReference<IRegion>> _dictionary =
			new Dictionary<string, WeakReference<IRegion>>();

		public event MapChangedEventHandler<string, IRegion> MapChanged;

		private void InvokeMapChanged(CollectionChange change, string key)
		{
			var eventHandler = MapChanged;
			if (eventHandler != null)
			{
				eventHandler(this,
				             new RegionMapChangedEventArgs(CollectionChange.ItemInserted, key));
			}
		}

		public IEnumerator<KeyValuePair<string, IRegion>> GetEnumerator()
		{
			return new RegionMapEnumerator(this);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public void Add(KeyValuePair<string, IRegion> item)
		{
			Add(item.Key, item.Value);
		}

		public void Clear()
		{
			this._dictionary.Clear();

			this.InvokeMapChanged(CollectionChange.Reset, string.Empty);
		}

		public bool Contains(KeyValuePair<string, IRegion> item)
		{
			return ContainsKey(item.Key);
		}

		public void CopyTo(KeyValuePair<string, IRegion>[] array, int arrayIndex)
		{
			if (array.Length - arrayIndex < _dictionary.Count)
			{
				throw new Exception("Array length to short");
			}

			int i = 0;

			foreach (KeyValuePair<string, IRegion> keyValuePair in this)
			{
				array[i + arrayIndex] = keyValuePair;

				i++;
			}
		}

		public bool Remove(KeyValuePair<string, IRegion> item)
		{
			return Remove(item.Key);
		}

		public int Count
		{
			get { return _dictionary.Count; }
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public void Add(string key, IRegion value)
		{
			_dictionary.Add(key, new WeakReference<IRegion>(value));

			InvokeMapChanged(CollectionChange.ItemInserted, key);
		}

		public bool ContainsKey(string key)
		{
			return _dictionary.ContainsKey(key);
		}

		[DefaultOverload]
		public bool Remove(string key)
		{
			bool returnValue = _dictionary.Remove(key);

			if (returnValue)
			{
				InvokeMapChanged(CollectionChange.ItemRemoved, key);
			}

			return returnValue;
		}

		public bool TryGetValue(string key, out IRegion value)
		{
			WeakReference<IRegion> region;
			bool returnValue = _dictionary.TryGetValue(key, out region);

			if (returnValue)
			{
				returnValue = region.TryGetTarget(out value);
			}
			else
			{
				value = null;
			}

			return returnValue;
		}

		public IRegion this[string key]
		{
			get
			{
				IRegion returnValue = null;

				TryGetValue(key, out returnValue);

				return returnValue;
			}
			set
			{
				bool setValue = true;

				WeakReference<IRegion> region;

				if (_dictionary.TryGetValue(key, out region))
				{
					IRegion outValue;

					if (region.TryGetTarget(out outValue) && outValue == value)
					{
						setValue = false;
					}
				}

				if (setValue)
				{
					_dictionary[key] = new WeakReference<IRegion>(value);

					InvokeMapChanged(CollectionChange.ItemChanged, key);
				}
			}
		}

		ICollection<string> IDictionary<string, IRegion>.Keys
		{
			get { return new List<string>(_dictionary.Keys); }
		}

		ICollection<IRegion> IDictionary<string, IRegion>.Values
		{
			get
			{
				List<IRegion> returnValue = new List<IRegion>();
				List<string> keys = new List<string>(_dictionary.Keys);

				foreach (string key in keys)
				{
					IRegion region = this[key];

					if (region != null)
					{
						returnValue.Add(region);
					}
					else
					{
						Remove(key);
					}
				}

				return returnValue;
			}
		}

		public IEnumerable<string> Keys
		{
			get { return ((IDictionary<string, IRegion>)this).Keys; }
		}

		public IEnumerable<IRegion> Values
		{
			get { return ((IDictionary<string, IRegion>)this).Values; }
		}

		public IRegion FindRegion(string name)
		{
			return FindRegion(name, null);
		}

		public IRegion FindRegion(string name, string context)
		{
			IRegion returnValue = null;
			string fullName = name;

			if (!string.IsNullOrEmpty(context))
			{
				fullName = string.Concat(name, '|', context);
			}

			TryGetValue(fullName, out returnValue);

			return returnValue;
		}

		public bool TryAdd(string fullName, IRegion region)
		{
			bool returnValue = true;
			WeakReference<IRegion> regionRef;

			if (_dictionary.TryGetValue(fullName, out regionRef))
			{
				IRegion temp;

				if (regionRef.TryGetTarget(out temp))
				{
					returnValue = false;
				}
			}

			if (returnValue)
			{
				_dictionary[fullName] = new WeakReference<IRegion>(region);
			}

			return returnValue;
		}
	}

	internal sealed class RegionMapEnumerator : IEnumerator<KeyValuePair<string, IRegion>>
	{
		private IEnumerator<string> enumerator;
		private RegionMap regionMap;
		private IRegion currentRegion;
		private string currentKey;

		public RegionMapEnumerator(RegionMap regionMap)
		{
			this.regionMap = regionMap;
			enumerator = regionMap.Keys.GetEnumerator();
		}

		public bool MoveNext()
		{
			bool returnValue = enumerator.MoveNext();

			while (returnValue)
			{
				currentKey = enumerator.Current;

				if (regionMap.TryGetValue(currentKey, out currentRegion))
				{
					break;
				}

				returnValue = enumerator.MoveNext();
			}

			return returnValue;
		}

		public void Reset()
		{
			enumerator.Reset();
		}

		public KeyValuePair<string, IRegion> Current
		{
			get
			{
				return
					new KeyValuePair<string, IRegion>(currentKey, currentRegion);
			}
		}

		object IEnumerator.Current
		{
			get { return Current; }
		}

		public void Dispose()
		{
			enumerator = null;
			regionMap = null;
		}
	}
}